package blogger

import (
	"binterfaces/ilogger"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"
)

type (

	// LoggerFormat 日志格式
	LoggerFormat int

	// BootLogger 日志实现
	BootLogger struct {
		option *LoggerOption
		size   int64
		file   *os.File
	}
)

// isLevelEnabled 检查日志记录器的日志级别是否大于设置的level参数
func (b *BootLogger) isLevelEnabled(level ilogger.LogLevel) bool {
	return b.option.Level <= level
}

func (b *BootLogger) getEntry() ilogger.IEntry {
	entry, ok := b.option.entryPool.Get().(ilogger.IEntry)
	if ok {
		return entry
	} else {
		entry = NewEntry(b)
		b.option.entryPool.Put(entry)
	}
	return entry
}

func (b *BootLogger) releaseEntry(entry ilogger.IEntry) {
	entry.Clear()
	b.option.entryPool.Put(entry)
}

// logf 带格式化的日志
func (b *BootLogger) logf(level ilogger.LogLevel, format string, args ...any) {
	if b.isLevelEnabled(level) {
		entry := b.getEntry()
		entry.LogF(level, format, args...)
		b.releaseEntry(entry)
	}
}

// logf 带格式化的日志
func (b *BootLogger) log(level ilogger.LogLevel, args ...any) {
	if b.isLevelEnabled(level) {
		entry := b.getEntry()
		entry.Log(level, args...)
		b.releaseEntry(entry)
	}
}

func (b *BootLogger) GetFormatter() ilogger.IFormatter {
	return b.option.Formatter
}

func (b *BootLogger) GetOutput() io.Writer {
	return b.option.out
}

func (b *BootLogger) GetLock() ilogger.ILock {
	return b.option.lock
}

func (b *BootLogger) GetPrefix() string {
	return b.option.Prefix
}

func (b *BootLogger) GetBufPool() ilogger.IBufferPool {
	if b.option.bufPool == nil {
		b.option.bufPool = DefaultBufPool()
	}
	return b.option.bufPool
}

func (b *BootLogger) IsCaller() bool {
	return b.option.ReportCaller
}

func (b *BootLogger) GetHooks() ilogger.IHook {
	return b.option.Hooks
}

func (b *BootLogger) Size() int64 {
	return b.size
}

func (b *BootLogger) AppendSize(size int64) {
	b.size = b.size + size
}

func (b *BootLogger) Max() int64 {
	return int64(b.option.MaxSize * 1024 * 1024)
}

// Rotate 转换
func (b *BootLogger) Rotate() error {
	if err := b.file.Close(); err != nil {
		return err
	}
	if err := b.openNew(); err != nil {
		return err
	}
	return nil
}

func (b *BootLogger) openNew() error {
	mode := os.FileMode(0600)
	name := b.option.filename
	info, err := os.Stat(b.option.filename)
	if err == nil {
		mode = info.Mode()
		newname := backupName(name, true)
		if err := os.Rename(name, newname); err != nil {
			return fmt.Errorf("can't rename log file: %s", err)
		}
	}
	f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
	if err != nil {
		return fmt.Errorf("can't open new logfile: %s", err)
	}
	b.file = f
	b.size = 0
	b.option.out = io.MultiWriter(os.Stdout, b.file)
	return nil
}

func backupName(name string, local bool) string {
	dir := filepath.Dir(name)
	filename := filepath.Base(name)
	ext := filepath.Ext(filename)
	prefix := filename[:len(filename)-len(ext)]
	t := time.Now()
	if !local {
		t = t.UTC()
	}
	timestamp := t.Format("2006-01-02T15-04-05.000")
	return filepath.Join(dir, fmt.Sprintf("%s-%s%s", prefix, timestamp, ext))
}

func (b *BootLogger) Close() error {
	b.GetLock().Lock()
	defer b.GetLock().UnLock()
	if b.file == nil {
		return nil
	}
	err := b.file.Close()
	b.file = nil
	return err
}
